package cn.tedu.csmall.passport.service.impl;

import cn.tedu.csmall.passport.ex.ServiceCode;
import cn.tedu.csmall.passport.ex.ServiceException;
import cn.tedu.csmall.passport.mapper.AdminMapper;
import cn.tedu.csmall.passport.mapper.AdminRoleMapper;
import cn.tedu.csmall.passport.mapper.RoleMapper;
import cn.tedu.csmall.passport.pojo.dto.AdminAddNewDTO;
import cn.tedu.csmall.passport.pojo.dto.AdminLoginInfoDTO;
import cn.tedu.csmall.passport.pojo.dto.AdminUpdateInfoDTO;
import cn.tedu.csmall.passport.pojo.entity.Admin;
import cn.tedu.csmall.passport.pojo.entity.AdminRole;
import cn.tedu.csmall.passport.pojo.vo.AdminListItemVO;
import cn.tedu.csmall.passport.pojo.vo.AdminStandardVO;
import cn.tedu.csmall.passport.security.AdminDetails;
import cn.tedu.csmall.passport.service.IAdminService;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 处理管理员相关业务的实现类
 *
 * @author java@tedu.cn
 * @version 0.0.1
 */
@Slf4j
@Service
public class AdminServiceImpl implements IAdminService {

    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private AdminRoleMapper adminRoleMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Value("${csmall.jwt.secret-key}")
    private String secretKey;
    @Value("${csmall.jwt.duration-in-minute}")
    private Long durationInMinute;

    public AdminServiceImpl() {
        log.debug("创建业务对象：AdminServiceImpl");
    }

    @Override
    public String login(AdminLoginInfoDTO adminLoginInfoDTO) {
        log.debug("开始处理【登录认证】的业务，参数：{}", adminLoginInfoDTO);

        // 调用AuthenticationManager的authenticate()方法执行认证
        // 在authenticate()方法的执行过程中
        // Spring Security会自动调用UserDetailsService对象的loadUserByUsername()获取用户详情
        // 并根据loadUserByUsername()返回的用户详情自动验证是否启用、判断密码是否正确等
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(
                adminLoginInfoDTO.getUsername(),
                adminLoginInfoDTO.getPassword());
        Authentication authenticateResult
                = authenticationManager.authenticate(authentication);
        log.debug("Spring Security已经完成认证，且认证通过，返回的结果：{}", authenticateResult);
        log.debug("返回认证信息中的当事人（Principal）类型：{}", authenticateResult.getPrincipal().getClass().getName());
        log.debug("返回认证信息中的当事人（Principal）数据：{}", authenticateResult.getPrincipal());

        // 从认证返回结果中取出当事人信息
        AdminDetails principal = (AdminDetails) authenticateResult.getPrincipal();
        Long id = principal.getId();
        log.debug("认证信息中的用户id：{}", id);
        String username = principal.getUsername();
        log.debug("认证信息中的用户名：{}", username);
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        log.debug("认证信息中的权限：{}", authorities);
        String authorityListString = JSON.toJSONString(authorities);
        log.debug("认证信息中的权限转换为JSON字符串：{}", authorityListString);

        // 生成JWT，并返回
        // 准备Claims值
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", id);
        claims.put("username", username);
        claims.put("authorities", authorityListString);

        // JWT的过期时间
        Date expiration = new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000);
        log.debug("即将生成JWT数据，过期时间：{}", expiration);

        // JWT的组成：Header（头：算法和Token类型）、Payload（载荷）、Signature（签名）
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                .setExpiration(expiration)
                // Signature
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        log.debug("已经生成JWT数据：{}", jwt);
        return jwt;
    }

    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        log.debug("开始处理【添加管理员】的业务，参数：{}", adminAddNewDTO);
        // 从参数中取出用户名
        String username = adminAddNewDTO.getUsername();
        log.debug("检查用户名【{}】是否被占用……", username);
        // 调用Mapper对象的countByUsername()根据用户名统计管理员账号的数量
        int countByUsername = adminMapper.countByUsername(username);
        // 判断统计结果是否大于0
        if (countByUsername > 0) {
            // 是：抛出ServiceException（ERR_CONFLICT）
            String message = "添加管理员失败，用户名【" + username + "】已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // 从参数中取出手机号码
        String phone = adminAddNewDTO.getPhone();
        log.debug("检查手机号码【{}】是否被占用……", phone);
        // 调用Mapper对象的countByPhone()根据手机号码统计管理员账号的数量
        int countByPhone = adminMapper.countByPhone(phone);
        // 判断统计结果是否大于0
        if (countByPhone > 0) {
            // 是：抛出ServiceException（ERR_CONFLICT）
            String message = "添加管理员失败，手机号码【" + phone + "】已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // 从参数中取出电子邮箱
        String email = adminAddNewDTO.getEmail();
        log.debug("检查电子邮箱【{}】是否被占用……", email);
        // 调用Mapper对象的countByEmail()根据电子邮箱统计管理员账号的数量
        int countByEmail = adminMapper.countByEmail(email);
        // 判断统计结果是否大于0
        if (countByEmail > 0) {
            // 是：抛出ServiceException（ERR_CONFLICT）
            String message = "添加管理员失败，电子邮箱【" + email + "】已经被占用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // 创建Admin对象，将作为插入时的参数
        Admin admin = new Admin();
        // 将参数的各属性复制到Admin对象中：BeanUtils.copyProperties()
        BeanUtils.copyProperties(adminAddNewDTO, admin);
        // 补全Admin对象的属性：loginCount，值为0
        admin.setLoginCount(0);
        // 取出Admin对象中的密码，将其加密，再存入到Admin对象中
        String rawPassword = admin.getPassword();
        String encodedPassword = passwordEncoder.encode(rawPassword);
        admin.setPassword(encodedPassword);
        // 调用Mapper对象的insert()插入管理员数据，并获取返回值
        log.debug("即将插入管理员数据：{}", admin);
        int rows = adminMapper.insert(admin);
        // 判断返回值是否不等于1
        if (rows != 1) {
            // 是：抛出ServiceException（ERR_INSERT）
            String message = "添加管理员失败，服务器忙，请稍后再尝试！[错误代码：1]";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }

        // 检查各角色id是否存在
        log.debug("检查各角色id是否存在");
        Long[] roleIds = adminAddNewDTO.getRoleIds();
        int countByIds = roleMapper.countByIds(roleIds);
        if (countByIds != roleIds.length) {
            // 是：抛出ServiceException（ERR_BAD_REQUEST）
            String message = "添加管理员失败，角色数据错误！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_BAD_REQUEST, message);
        }

        // 检查角色id，不允许包含id=1的角色，且角色id至少有1个
        log.debug("原角色id数组：{}", Arrays.toString(roleIds));
        Arrays.sort(roleIds);
        if (roleIds[0] == 1L) {
            roleIds[0] = roleIds[roleIds.length - 1];
            roleIds = Arrays.copyOf(roleIds, roleIds.length - 1);
            log.debug("角色id数组中不允许包含id=1的值，处理后的结果：{}", Arrays.toString(roleIds));
        }
        if (roleIds.length == 0) {
            // 是：抛出ServiceException（ERR_INSERT）
            String message = "添加管理员失败，服务器忙，请稍后再尝试！[错误代码：2]";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }

        // 插入管理员与角色的关联关系
        log.debug("准备插入管理员与角色的关联关系");
        LocalDateTime now = LocalDateTime.now();
        List<AdminRole> adminRoleList = new ArrayList<>();
        for (int i = 0; i < roleIds.length; i++) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleIds[i]);
            adminRole.setGmtCreate(now);
            adminRole.setGmtModified(now);
            adminRoleList.add(adminRole);
        }
        rows = adminRoleMapper.insertBatch(adminRoleList);
        if (rows != roleIds.length) {
            // 是：抛出ServiceException（ERR_INSERT）
            String message = "添加管理员失败，服务器忙，请稍后再尝试！[错误代码：3]";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        log.debug("添加管理员完成！");
    }

    @Override
    public void deleteById(Long id) {
        log.debug("开始处理【根据id删除管理员】的业务：id={}", id);
        // 不允许对id=1的管理员进行危险管理操作，返回NOT_FOUND错误，视为id=1的管理员不存在
        if (id == 1) {
            // 抛出ServiceException，业务状态码：40400
            String message = "删除管理员失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 调用adminMapper根据参数id执行查询
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 抛出ServiceException，业务状态码：40400
            String message = "删除管理员失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 调用adminMapper根据参数id删除管理员的数据，并获取返回值
        int rows = adminMapper.deleteById(id);
        // 判断返回值是否不为1
        if (rows != 1) {
            // 抛出ServiceException，业务状态码：DELETE对应的常量
            String message = "删除管理员失败！服务器忙，请稍后再次尝试！[错误代码：1]";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }

        // 删除“管理员与角色”的关联数据
        rows = adminRoleMapper.deleteByAdmin(id);
        // 判断返回值是否小于1
        if (rows < 1) {
            // 抛出ServiceException，业务状态码：DELETE对应的常量
            String message = "删除管理员失败！服务器忙，请稍后再次尝试！[错误代码：2]";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_DELETE, message);
        }
    }

    @Override
    public void updatePasswordById(Long id, String password) {
        log.debug("开始处理【修改管理员密码】的业务，id={}，password={}", id, password);
        // 不允许对id=1的管理员进行危险管理操作，返回NOT_FOUND错误，视为id=1的管理员不存在
        if (id == 1) {
            // 抛出ServiceException，业务状态码：40400
            String message = "修改管理员密码失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 调用adminMapper根据参数id执行查询
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 抛出ServiceException，业务状态码：40400
            String message = "修改管理员密码失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 创建Admin对象，将作为修改时的参数
        Admin admin = new Admin();
        admin.setId(id);
        // 取出Admin对象中的密码，将其加密，再存入到Admin对象中
        String encodedPassword = passwordEncoder.encode(password);
        admin.setPassword(encodedPassword);
        // 调用Mapper对象的update()修改管理员密码，并获取返回值
        log.debug("即将修改管理员密码：{}", admin);
        int rows = adminMapper.update(admin);
        // 判断返回值是否不等于1
        if (rows != 1) {
            // 是：抛出ServiceException（ERR_INSERT）
            String message = "修改管理员密码失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }

    @Override
    public void updateInfoById(Long id, AdminUpdateInfoDTO adminUpdateInfoDTO) {
        log.debug("开始处理【修改管理员基本资料】的业务，id={}，新基本资料={}", adminUpdateInfoDTO);
        // 不允许对id=1的管理员进行危险管理操作，返回NOT_FOUND错误，视为id=1的管理员不存在
        if (id == 1) {
            // 抛出ServiceException，业务状态码：40400
            String message = "修改管理员基本资料失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 调用adminMapper根据参数id执行查询
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 抛出ServiceException，业务状态码：40400
            String message = "修改管理员基本资料失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 创建Admin对象，将作为修改时的参数
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminUpdateInfoDTO, admin);
        admin.setId(id);
        // 调用Mapper对象的update()修改管理员基本资料，并获取返回值
        log.debug("即将修改管理员基本资料：{}", admin);
        int rows = adminMapper.update(admin);
        // 判断返回值是否不等于1
        if (rows != 1) {
            // 是：抛出ServiceException（ERR_INSERT）
            String message = "修改管理员基本资料失败，服务器忙，请稍后再尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }

    @Override
    public void setEnable(Long id) {
        log.debug("开始处理【启用管理员账号】的业务：id={}", id);
        // 不允许对id=1的管理员进行危险管理操作，返回NOT_FOUND错误，视为id=1的管理员不存在
        if (id == 1) {
            // 抛出ServiceException，业务状态码：40400
            String message = "启用管理员账号失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 根据id查询管理员数据
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 是：ServiceException：NOT_FOUND
            String message = "启用管理员账号失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 判断查询结果中的enable是否为1
        if (queryResult.getEnable() == 1) {
            // 是：ServiceException：CONFLICT
            String message = "启用管理员账号失败！当前账号已经启用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // Admin admin = new Admin(); admin.setId(id); admin.setEnable(1);
        Admin admin = new Admin();
        admin.setId(id);
        admin.setEnable(1);
        // 执行更新，获取返回值
        int rows = adminMapper.update(admin);
        // 判断返回值是否不为1
        if (rows != 1) {
            // 是：ServiceException：UPDATE
            String message = "启用管理员账号失败！服务器忙，请稍后再次尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }

    @Override
    public void setDisable(Long id) {
        log.debug("开始处理【禁用管理员账号】的业务：id={}", id);
        // 不允许对id=1的管理员进行危险管理操作，返回NOT_FOUND错误，视为id=1的管理员不存在
        if (id == 1) {
            // 抛出ServiceException，业务状态码：40400
            String message = "禁用管理员账号失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 根据id查询管理员数据
        AdminStandardVO queryResult = adminMapper.getStandardById(id);
        // 判断查询结果是否为null
        if (queryResult == null) {
            // 是：ServiceException：NOT_FOUND
            String message = "禁用管理员账号失败！尝试访问的数据不存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_NOT_FOUND, message);
        }

        // 判断查询结果中的enable是否为1
        if (queryResult.getEnable() == 0) {
            // 是：ServiceException：CONFLICT
            String message = "禁用管理员账号失败！当前账号已经禁用！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // Admin admin = new Admin(); admin.setId(id); admin.setEnable(1);
        Admin admin = new Admin();
        admin.setId(id);
        admin.setEnable(0);
        // 执行更新，获取返回值
        int rows = adminMapper.update(admin);
        // 判断返回值是否不为1
        if (rows != 1) {
            // 是：ServiceException：UPDATE
            String message = "禁用管理员账号失败！服务器忙，请稍后再次尝试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_UPDATE, message);
        }
    }

    @Override
    public List<AdminListItemVO> list() {
        log.debug("开始处理【查询管理员列表】的业务……");
        List<AdminListItemVO> list = adminMapper.list();
        // 移除id=1的管理员数据，使得其不显示到前端的列表中
        Iterator<AdminListItemVO> iterator = list.iterator();
        while (iterator.hasNext()) {
            AdminListItemVO admin = iterator.next();
            if (admin.getId() == 1L) {
                iterator.remove();
                break;
            }
        }
        log.debug("即将返回【{}条】管理员数据……", list.size());
        for (Object item : list) {
            log.debug("{}", item);
        }
        return list;
    }

}
